import os
import time
import argparse
import logging
import json
import signal
import threading
from datetime import datetime
from modules.login import LoginManager
from modules.api_client import ApiClient
from modules.scheduler import TaskScheduler
from modules.logger_config import setup_logger

# 配置文件路径
CONFIG_PATH = "config.ini"


def log_task_start(task_name):
    """记录任务开始的辅助函数"""
    timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    logger.info(f"===== 开始执行{task_name}任务: {timestamp} =====")
    return datetime.now()


def log_task_end(task_name, start_time):
    """记录任务结束的辅助函数"""
    duration = (datetime.now() - start_time).total_seconds()
    timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    logger.info(f"===== {task_name}任务完成: {timestamp} (耗时: {duration:.2f}秒) =====")


def format_datetime(obj):
    """将datetime对象转换为可读字符串的辅助函数"""
    if isinstance(obj, datetime):
        return obj.strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
    elif isinstance(obj, dict):
        return {key: format_datetime(value) for key, value in obj.items()}
    elif isinstance(obj, list):
        return [format_datetime(item) for item in obj]
    return obj


def format_status_for_logging(status):
    """格式化任务状态以便于日志记录"""
    # 深拷贝并格式化所有datetime对象
    formatted_status = format_datetime(status)
    # 使用json.dumps确保中文和特殊字符正确显示
    return json.dumps(formatted_status, ensure_ascii=False, indent=2)


def execute_single_task(login_manager, api_client, interface_type, auto_close=False, login=True):
    """执行单个任务（配属或大修）"""
    interface_name = "配属" if interface_type == 'peishu' else "大修"
    task_id = f"{interface_type}_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
    
    # 任务状态跟踪
    status = {
        'task_id': task_id,
        'interface_type': interface_type,
        'start_time': datetime.now(),
        'steps': [],
        'success': False
    }
    
    logger.info(f"任务[{task_id}]开始执行{interface_name}")
    
    # 检查是否已经登录
    if login:
        logger.info(f"{interface_name}任务：执行登录操作...")
        status['steps'].append({'step': 'login', 'start_time': datetime.now()})
        
        # 添加登录重试机制
        max_login_retries = 2
        login_success = False
        for retry in range(max_login_retries):
            if login_manager.perform_login(auto_close=auto_close):
                login_success = True
                break
            logger.warning(f"{interface_name}任务：登录失败，第 {retry+1}/{max_login_retries} 次重试...")
            time.sleep(3)
        
        status['steps'][-1]['end_time'] = datetime.now()
        status['steps'][-1]['duration'] = (status['steps'][-1]['end_time'] - status['steps'][-1]['start_time']).total_seconds()
        
        if not login_success:
            logger.error(f"{interface_name}任务：登录失败，已重试{max_login_retries}次，任务终止")
            status['success'] = False
            error_message = "登录失败"
            status['error'] = error_message
            logger.error(f"{interface_name}任务：{error_message}")
            logger.info(f"任务[{task_id}]状态:\n{format_status_for_logging(status)}")
            return False
    
    # 调用后端接口（带重试机制）
    status['steps'].append({'step': 'call_backend', 'start_time': datetime.now()})
    max_retries = 3
    backend_data = None
    
    for retry in range(max_retries):
        start_time = datetime.now()
        backend_data = api_client.call_backend(interface_type)
        if backend_data:
            call_time = (datetime.now() - start_time).total_seconds()
            logger.info(f"{interface_name}接口调用耗时: {call_time:.2f}秒")
            logger.debug(f"{interface_name}接口返回数据: {backend_data}")
            break
        logger.warning(f"{interface_name}接口调用失败，第 {retry+1}/{max_retries} 次重试...")
        time.sleep(2)
    
    status['steps'][-1]['end_time'] = datetime.now()
    status['steps'][-1]['duration'] = (status['steps'][-1]['end_time'] - status['steps'][-1]['start_time']).total_seconds()
    
    if not backend_data:
        logger.error(f"{interface_name}接口调用失败，已重试{max_retries}次，任务终止")
        status['success'] = False
        status['error'] = "接口调用失败"
        logger.info(f"任务[{task_id}]状态:\n{format_status_for_logging(status)}")
        return False
    
    # 推送数据（带重试机制）
    status['steps'].append({'step': 'push_data', 'start_time': datetime.now()})
    push_success = False
    
    for retry in range(max_retries):
        start_time = datetime.now()
        if api_client.push_data(backend_data, interface_type):
            push_time = (datetime.now() - start_time).total_seconds()
            logger.info(f"{interface_name}数据推送耗时: {push_time:.2f}秒")
            push_success = True
            break
        logger.warning(f"{interface_name}数据推送失败，第 {retry+1}/{max_retries} 次重试...")
        time.sleep(2)
    
    status['steps'][-1]['end_time'] = datetime.now()
    status['steps'][-1]['duration'] = (status['steps'][-1]['end_time'] - status['steps'][-1]['start_time']).total_seconds()
    
    if not push_success:
        logger.error(f"{interface_name}数据推送失败，已重试{max_retries}次，任务终止")
        status['success'] = False
        status['error'] = "数据推送失败"
        logger.info(f"任务[{task_id}]状态:\n{format_status_for_logging(status)}")
        return False
    
    # 任务完成
    status['end_time'] = datetime.now()
    status['duration'] = (status['end_time'] - status['start_time']).total_seconds()
    status['success'] = True
    
    logger.info(f"任务[{task_id}]完成，总耗时: {status['duration']:.2f}秒")
    logger.debug(f"任务[{task_id}]详细状态:\n{format_status_for_logging(status)}")
    return True


def execute_both_tasks(login_manager, api_client, auto_close=False):
    """执行两个任务（配属和大修），只登录一次"""
    start_time = log_task_start("双任务流程")
    browser_used = False
    
    try:
        # 先检查是否已经登录，如果没有则执行登录
        logger.info("执行登录操作...")
        browser_used = True
        
        # 登录重试机制
        max_login_retries = 2
        login_success = False
        for retry in range(max_login_retries):
            # 注意：传递auto_close=True，让浏览器在任务线程内自己管理关闭
            if login_manager.perform_login(auto_close=auto_close):
                login_success = True
                break
            logger.warning(f"登录失败，第 {retry+1}/{max_login_retries} 次重试...")
            time.sleep(3)
        
        if not login_success:
            logger.error("登录失败，任务终止")
            return False
        
        # 依次执行两个任务，共享登录状态
        # 注意：第一个任务也应该设置auto_close=True，如果两个任务都需要关闭的话
        task1_success = execute_single_task(login_manager, api_client, 'peishu', auto_close=auto_close, login=False)
        task2_success = execute_single_task(login_manager, api_client, 'daxiu', auto_close=auto_close, login=False)
        
        return task1_success and task2_success
    
    except Exception as e:
        logger.error(f"双任务执行异常: {str(e)}", exc_info=True)
        return False
    
    finally:
        # 移除主线程中的close_browser调用，避免greenlet切换错误
        # 浏览器资源将在创建它的线程中由perform_login的auto_close参数控制关闭
        log_task_end("双任务流程", start_time)


def main_task_wrapper(login_manager, api_client, interface_type=None, auto_close=True):
    """任务包装器，根据interface_type参数决定执行单个任务还是双任务
    interface_type: str - 'peishu'(配属)、'daxiu'(大修) 或 None(双任务)
    """
    try:
        if interface_type is None:
            # 定时任务：执行双任务
            return execute_both_tasks(login_manager, api_client, auto_close=auto_close)
        else:
            # 激活任务：执行单个任务
            # auto_close=True会确保任务在自己的线程中关闭浏览器
            return execute_single_task(login_manager, api_client, interface_type, auto_close=auto_close)
    
    except Exception as e:
        logger.error(f"任务执行异常: {str(e)}", exc_info=True)
        return False
    
    finally:
        # 移除主线程中的close_browser调用，避免greenlet切换错误
        # 浏览器资源将在创建它的线程中由perform_login和execute_single_task的auto_close参数控制关闭
        pass


def parse_args():
    """解析命令行参数"""
    parser = argparse.ArgumentParser(description='RPA自动化任务系统')
    parser.add_argument('--debug', action='store_true', help='启用调试日志')
    parser.add_argument('--no-initial-task', action='store_true', help='跳过初始化任务执行')
    return parser.parse_args()


if __name__ == "__main__":
    # 解析命令行参数
    args = parse_args()
    
    # 根据参数设置日志级别
    log_level = logging.DEBUG if args.debug else logging.INFO
    logger = setup_logger('Main', log_level)  # 传递日志级别
    
    logger.info(f"启动RPA自动化任务系统，日志级别: {'DEBUG' if args.debug else 'INFO'}")
    
    # 检查并创建默认配置文件
    if not os.path.exists(CONFIG_PATH):
        logger.warning(f"未找到配置文件 {CONFIG_PATH}，请确保文件存在")
        # 这里可以添加创建默认配置文件的逻辑
    else:
        logger.info(f"配置文件加载成功: {CONFIG_PATH}")
    
    # 初始化组件
    login_manager = LoginManager(CONFIG_PATH)
    api_client = ApiClient(CONFIG_PATH, login_manager)
    
    # 定义任务函数（绑定参数）
    def task_wrapper(interface_type=None, auto_close=False):
        return main_task_wrapper(login_manager, api_client, interface_type=interface_type, auto_close=auto_close)
    
    # 初始化调度器
    scheduler = TaskScheduler(CONFIG_PATH, task_wrapper)
    
    # 创建停止事件
    stop_event = threading.Event()
    
    # 设置信号处理函数，确保Ctrl+C能正确终止所有线程
    def signal_handler(sig, frame):
        logger.info("收到终止信号，正在安全关闭系统...")
        # 设置停止事件而不是直接抛出异常，避免递归中断问题
        stop_event.set()
    
    # 注册信号处理
    signal.signal(signal.SIGINT, signal_handler)  # 处理Ctrl+C
    signal.signal(signal.SIGTERM, signal_handler)  # 处理终止信号
    
    # 启动服务
    try:
        # 立即执行一次双任务（如果未禁用）
        if not args.no_initial_task:
            logger.info("立即执行初始化双任务...")
            task_wrapper(interface_type=None, auto_close=False)
        else:
            logger.info("跳过初始化任务执行")
        
        # 启动定时任务
        scheduler.start_schedule()
        
        # 启动激活服务器
        scheduler.start_activation_server()
        
        logger.info("RPA系统已完全启动，等待定时任务或激活请求...")
        logger.info("按Ctrl+C可以优雅地终止程序")
        
        # 使用事件等待，直到收到停止信号
        stop_event.wait()  # 等待直到收到终止信号
            
    except Exception as e:
        logger.error(f"系统运行异常: {str(e)}", exc_info=True)
    finally:
        # 确保资源释放
        try:
            logger.info("正在清理资源...")
            # 移除main函数中的close_browser调用，避免greenlet切换错误
            # 每个任务线程应该在创建浏览器时负责关闭它自己的浏览器实例
            scheduler.stop()
            logger.info("资源清理完成，程序退出")
        except Exception as e:
            logger.error(f"清理资源时发生错误: {str(e)}")